/*
	klib_system_timers.c

	provides functionality to register system timer drivers with the kernel

	Author: Aidan Goddard 21/10/13
*/

#include"headers/klib_system_timers.h"
#include"../kernel/headers/scheduler_functions.h"
#include"../kernel/headers/bakery_lock.h"

static SCH_CLS scheduler_timer_lock;
static void(*pause_timer_func)(void) = NULL;
static void(*start_timer_func)(void) = NULL;

// function to register a new handler for the system scheduler timer
uint32_t __KLIB__RegisterSchedulerTimer(void(*pause_timer)(void), void(*start_timer)(void))
{
	// check the given functions for valid pointers
	if(((uint64_t)pause_timer >= KOFFSET + 0x40000000) || ((uint64_t)pause_timer < KOFFSET))
	{
		return SCHEDULER_TIMER_INVALID_PTR;
	}
	if(((uint64_t)start_timer >= KOFFSET + 0x40000000) || ((uint64_t)start_timer < KOFFSET))
	{
		return SCHEDULER_TIMER_INVALID_PTR;
	}

	// grab lock on the table
	__asm__ __volatile__ ("":::"memory");
	__asm__ __volatile__ ("cli");
	__asm__ __volatile__ ("":::"memory");
	int CPU = GetCPUID();
	BakeryEnter(&scheduler_timer_lock, CPU);

	// check for values already in the table
	if((pause_timer_func != NULL) || (start_timer_func != NULL))
	{
		BakeryLeave(&scheduler_timer_lock, CPU);
		__asm__ __volatile__ ("":::"memory");
		__asm__ __volatile__ ("sti");
		__asm__ __volatile__ ("":::"memory");
		return SCHEDULER_TIMER_ALREADY_REGISTERED;
	}

	// save the new pointers
	pause_timer_func = pause_timer;
	start_timer_func = start_timer;

	// finished, leave lock
	BakeryLeave(&scheduler_timer_lock, CPU);
	__asm__ __volatile__ ("":::"memory");
	__asm__ __volatile__ ("sti");
	__asm__ __volatile__ ("":::"memory");

	// finished, return success
	return SCHEDULER_TIMER_SUCCESS;
}

// function to pause the system scheduler timer (use with caution!)
uint32_t __KLIB__PauseSchedulerTimer()
{
	// get lock on table
	__asm__ __volatile__ ("":::"memory");
	__asm__ __volatile__ ("cli");
	__asm__ __volatile__ ("":::"memory");
	int CPU = GetCPUID();
	BakeryEnter(&scheduler_timer_lock, CPU);

	// check if pause function is present
	if(pause_timer_func == NULL)
	{
		BakeryLeave(&scheduler_timer_lock, CPU);
		__asm__ __volatile__ ("":::"memory");
		__asm__ __volatile__ ("sti");
		__asm__ __volatile__ ("":::"memory");
		return SCHEDULER_TIMER_NONE_REGISTERED;
	}

	// call it
	(*pause_timer_func)();

	// release lock and return success
	BakeryLeave(&scheduler_timer_lock, CPU);
	__asm__ __volatile__ ("":::"memory");
	__asm__ __volatile__ ("sti");
	__asm__ __volatile__ ("":::"memory");
	return SCHEDULER_TIMER_SUCCESS;
}

// function to unpause the system scheduler timer
uint32_t __KLIB__StartSchedulerTimer()
{
	// get lock on table
	__asm__ __volatile__ ("":::"memory");
	__asm__ __volatile__ ("cli");
	__asm__ __volatile__ ("":::"memory");
	int CPU = GetCPUID();
	BakeryEnter(&scheduler_timer_lock, CPU);

	// check if start function is present
	if(start_timer_func == NULL)
	{
		BakeryLeave(&scheduler_timer_lock, CPU);
		__asm__ __volatile__ ("":::"memory");
		__asm__ __volatile__ ("sti");
		__asm__ __volatile__ ("":::"memory");
		return SCHEDULER_TIMER_NONE_REGISTERED;
	}

	// call it
	(*start_timer_func)();

	// release lock and return success
	BakeryLeave(&scheduler_timer_lock, CPU);
	__asm__ __volatile__ ("":::"memory");
	__asm__ __volatile__ ("sti");
	__asm__ __volatile__ ("":::"memory");
	return SCHEDULER_TIMER_SUCCESS;
}

// function to unregister the system scheduler timer
uint32_t __KLIB__UnregisterSchedulerTimer()
{
	// get lock on table
	__asm__ __volatile__ ("":::"memory");
	__asm__ __volatile__ ("cli");
	__asm__ __volatile__ ("":::"memory");
	int CPU = GetCPUID();
	BakeryEnter(&scheduler_timer_lock, CPU);

	// check if functions are present
	if((pause_timer_func == NULL) || (start_timer_func == NULL))
	{
		BakeryLeave(&scheduler_timer_lock, CPU);
		__asm__ __volatile__ ("":::"memory");
		__asm__ __volatile__ ("sti");
		__asm__ __volatile__ ("":::"memory");
		return SCHEDULER_TIMER_NONE_REGISTERED;
	}

	// delete them
	pause_timer_func = NULL;
	start_timer_func = NULL;

	// release lock and return success
	BakeryLeave(&scheduler_timer_lock, CPU);
	__asm__ __volatile__ ("":::"memory");
	__asm__ __volatile__ ("sti");
	__asm__ __volatile__ ("":::"memory");
	return SCHEDULER_TIMER_SUCCESS;
}
